module Main where
import Text.XML.Light.Input
import Text.XML.Light.Output
import Text.XML.Light.Types
import System.Environment (getArgs)
import System.Console.GetOpt
import Data.Maybe ( fromMaybe )
import System.Exit (ExitCode(..), exitWith)
import System.IO (hFlush, hPutStrLn, stderr, stdout)

xmlnsXsd = "http://www.w3.org/2001/XMLSchema"

data SqlField = SqlField { sqlFieldName :: String, sqlDataType :: String, sqlNullable :: Bool}

xsdTagName :: String -> QName
xsdTagName n = QName {qName = n,
        qURI = Just xmlnsXsd,
        qPrefix = Just "xsd"}
    
makePrefixedAttr p n v = Attr {attrKey = QName {qName = n, qURI=Nothing, qPrefix=p}, attrVal=v}

makePrefixedAttrList :: Maybe String -> [[String]] -> [Attr]
makePrefixedAttrList p xs = map (\[x,y] -> makePrefixedAttr p x y) xs

xsdSequence :: [Content] -> Element
xsdSequence content = Element {elName = xsdTagName "sequence", elAttribs = [], elLine=Nothing,
    elContent = content}

xsdAll :: [Content] -> Element
xsdAll content = Element {elName = xsdTagName "all", elAttribs = [], elLine=Nothing,
    elContent = content}
    
makeElemFromDDL :: SqlField -> Element
makeElemFromDDL s = xsdElement [["name", sqlFieldName s], ["type", dt]] doc []
    where
        doc = "source DB data type is " ++ sqlDataType s
        dt = case sqlDataType s of
--            "DATE" -> "xsd:date" 
--            "NUMBER" -> "xsd:string"
            _ -> "xsd:string"

xsdAnnoDoc :: String -> Element
xsdAnnoDoc s = Element {elName = xsdTagName "annotation", elAttribs = [], 
    elContent = [Elem doc], elLine = Nothing}
    where 
        doc = Element {elName = xsdTagName "documentation", elAttribs = [],
        elContent = [Text txt], elLine = Nothing}
        txt = CData {cdVerbatim = CDataText, cdData = s, cdLine = Nothing}

xsdElement :: [[String]] -> String -> [Content] -> Element    
xsdElement atpairs doc cnt = Element {elName = xsdTagName "element", 
    elAttribs = makePrefixedAttrList Nothing atpairs,
    elContent = [Elem $ xsdAnnoDoc doc] ++ cnt, 
    elLine = Nothing}

proceedTxtFile :: String -> [Flag] -> IO ()
proceedTxtFile fn opts = do
    txt <- readFile fn
    publish $ makeXSD mode namespace entity $ nvs txt
    where
        nvs txt = map parseDesc $ goodLines txt
        goodLine x = case words x of 
            [] -> False
            ("Name":("Type":_)) -> False
            (a:_)
                | '-' `elem` a -> False
                | otherwise -> True
        goodLines b = filter goodLine $ lines b
        namespace = case [x | NameSpace x <- opts] of
            [] -> "default"
            (z:_) -> z
        entity = case [x | Entity x <- opts] of
            [] -> "Default"
            (z:_) -> z
        publish cnt = case [x | OutputFileName x <- opts] of
            [] -> putStrLn cnt
            ["-"] -> putStrLn cnt
            (z:_) -> writeFile z cnt
        sender = Sender `elem` opts
        receiver = Receiver `elem` opts
        mode = if sender && receiver || sender
            then 's' 
            else if receiver then 'r' 
                else error "mode handling error in proceedTxtFile (no option sender/receiver given)"
        

parseDesc :: String -> SqlField
parseDesc x = SqlField {sqlFieldName = z!!0, sqlDataType = z!!1, sqlNullable = m}
    where 
        z = words x
        m = case z of
            _:_:t:_ -> t == "Y"
--            [f, s, t, _] -> t
--            [f, s, t] -> t
            _ -> False

makeXSD :: Char -> String -> String -> [SqlField] -> String
makeXSD 's' namespace entity xs = ppTopElement $ makeSender namespace entity $ map makeElemFromDDL xs

makeXSD _ namespace entity xs = error "Not implemented yet"

makeSender :: String -> String -> [Element] -> Element
makeSender ns entity xs = Element {elName = xsdTagName "schema", 
    elAttribs = [makePrefixedAttr Nothing "targetNamespace" ns, 
        makePrefixedAttr Nothing "xmlns" ns, 
        makePrefixedAttr (Just "xmlns") "xsd" xmlnsXsd],
    elContent = [Elem topElement, Elem ct, Elem ct2],
    elLine=Nothing}
    where
        topElement = xsdElement [["name", userMT], ["type", userDT]] "JDBC sender type" []
        ct = Element {elName = xsdTagName "complexType",
            elAttribs = [makePrefixedAttr Nothing "name" userDT],
            elContent = [Elem $ xsdSequence [Elem rowDec]], elLine = Nothing}
        rowDec = xsdElement [["maxOccurs", "unbounded"],
                ["minOccurs", "0"], ["name", "row"], ["type", userDTRow]] "row type" []
        ct2 = Element {elName = xsdTagName "complexType",
            elAttribs = [makePrefixedAttr Nothing "name" userDTRow],
            elContent = [Elem $ xsdAll elems], elLine = Nothing}
        elems = map Elem xs
        userDT = "DT_" ++ entity
        userMT = "MT_" ++ entity
        userDTRow = userDT ++ "_Row"

produceMappingFile :: String -> [Flag] -> IO ()
produceMappingFile fn opts = do
    stub <- readFile "xi_stub.xsl"
    txt <- readFile fn
    putStrLn $ concated $ lines txt
    where 
        concated xs = join ", $semi,\n" xs

join :: String -> [String] -> String
join s [] = ""
join s [a] = a
join s (l:ls) = l ++ s ++ (join s ls)
        
data Flag = Version | Help | Sender | Receiver | SrcKsss | MappingCSV
     | NameSpace String | Entity String | OutputFileName String 
       deriving (Show, Eq)

options :: [OptDescr Flag]
options =
 [ Option ['V'] ["version"]    (NoArg Version)            ""
 , Option ['h'] ["help"]       (NoArg Help)                "help"
 , Option ['n'] ["namespace"]  (ReqArg NameSpace "xmlns")  "namespace"
 , Option ['e'] ["entity"]     (ReqArg Entity    "thing")  "entity's name"
 , Option ['o'] ["output"]     (ReqArg OutputFileName "filename") "output filename"
 , Option ['s'] ["sender"]     (NoArg Sender) "make sender channel XSD"
 , Option ['r'] ["receiver"]   (NoArg Receiver) "make receiver channel XSD"
 , Option ['k'] ["src-ksss"]   (NoArg SrcKsss) "make receiver channel XSD from KSSS sorce"
 , Option [] ["mappingcsv"]    (NoArg MappingCSV)          "make mapping into CSV-like channel"
 ]
optionsUsage = "Usage: jdbcutil [OPTION...] filename" 

main :: IO ()
main = do
    argv <- getArgs
    case parse argv of
        ([], [], []) -> help
        (opts, [], errs)
            | Help `elem` opts -> help
            | Version `elem` opts -> version
            | otherwise -> die "source filename is required"
        (opts, (filename:[]), _) 
            | Help `elem` opts -> help
            | Version `elem` opts -> version
            | (SrcKsss `elem` opts) && not (null $ ofn opts) -> 
                    readFile filename >>= writeFile (ofn opts). ppTopElement . 
                            makeSender (ns opts) (ent opts) . map (\w -> makeElemFromDDL (SqlField w "" True)) . words . head . lines
            | (MappingCSV `elem` opts) -> produceMappingFile filename opts
            | (Receiver `elem` opts) || (Sender `elem` opts) -> proceedTxtFile filename opts
            | otherwise -> die "Error: sender/receiver mode unspecified"
    where
        parse a = getOpt Permute options a 
        info = usageInfo optionsUsage options
        dump = hPutStrLn stderr
        -- die errs = dump (concat errs ++ info) >> exitWith (ExitFailure 1)
        die errtxt = dump errtxt >> exitWith (ExitFailure 1)
        help = dump info >> exitWith ExitSuccess
        version = dump "version here" >> exitWith ExitSuccess
        --
        ofn opts = concat $ take 1 [fn | OutputFileName fn <- opts] 
        ns opts = concat $ take 1 [fn | NameSpace fn <- opts] 
        ent opts = concat $ take 1 [fn | Entity fn <- opts] 
    
{-
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema targetNamespace="http://lukoil.ru/ds/petronics/assign"
  xmlns="http://lukoil.ru/ds/petronics/assign" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:element name="MT_Assigns" type="DT_Assigns" />
  <xsd:complexType name="DT_Assigns">
    <xsd:annotation>
    </xsd:annotation>
    <xsd:sequence>
      <xsd:element maxOccurs="unbounded" minOccurs="0" name="row" type="DT_Assign_Row"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <xsd:annotation>
        </xsd:annotation>
      </xsd:element>
    </xsd:sequence>
  </xsd:complexType>
  <xsd:complexType name="DT_Assign_Row">
    <xsd:annotation>
    </xsd:annotation>
    <xsd:sequence>
      <xsd:element name="ZZZ_SUPP_TREATY_ID" type="xsd:string">
        <xsd:annotation>
        </xsd:annotation>
      </xsd:element>
    </xsd:sequence>
  </xsd:complexType>
</xsd:schema>

-}